from collections import defaultdict
from datetime import datetime

from odoo import models, fields, api, _, SUPERUSER_ID
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare, float_is_zero, float_round, DEFAULT_SERVER_DATETIME_FORMAT


# 采购类
class PurchaseOrder(models.Model):
    _inherit = 'purchase.order'

    state = fields.Selection(selection_add=[('draft', 'Not approved'), ('to confirm', 'to confirm')])
    sb_workflow_template_id = fields.Many2one(
        'sb.workflow.template', string="Approval Workflow Template",
        domain="[('sb_workflow_template_item_ids', '!=', False), ('workflow_type', 'in', ['other','purchase'])]"
    )

    sb_workflow_id = fields.Many2one('sb.workflow', string="Current Approval Workflow", copy=False)
    sb_workflow_state = fields.Selection(related="sb_workflow_id.state", readonly=True, string="Approval Status",
                                         store=True)
    sb_workflow_item_ids = fields.One2many(related="sb_workflow_id.sb_workflow_item_ids", string="Approval Items")
    sb_workflow_attachments_ids = fields.One2many(
        related="sb_workflow_id.sb_workflow_attachments_ids", string="Approval Attachments")

    sb_workflow_ids = fields.One2many('sb.workflow', 'purchase_order_id', string="Approval Workflows")
    sb_workflow_ids_sum = fields.Integer(compute="_compute_sb_workflow_ids_sum", store=True)
    requisition_id = fields.Many2one('purchase.requisition', string="Purchase Requisition")
    requisition_ids = fields.Many2many('purchase.requisition',
                                       column1='po_id',
                                       column2='pr_id',
                                       relation='purchase_order_requisition_id_rel',
                                       string="Purchase Requisition")
    supplement_order = fields.Boolean(string='Supplement', compute='_compute_supplement_order')
    s_date_planned = fields.Date(string='Expected Arrival', copy=False, tracking=True)
    s_date_approve = fields.Date(string='Approval Date', copy=False, tracking=True)
    s_date_order = fields.Date(string='Order Deadline', copy=False, tracking=True)
    s_remark = fields.Text(string='Remark')

    s_printed = fields.Boolean(string='Printed', default=False, copy=False, readonly=True)
    # 就是采购员要追踪自己哪些请购单没有, 没有交完货的
    s_user_purchase_pickings = fields.Boolean(
        string='Not Pickings Complete', compute='_compute_get_purchase_picking', store=True
    )

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        res.onchange_partner_po()
        return res

    @api.constrains('requisition_id')
    def constrains_requisition_id(self):
        for rec in self:
            if not rec.requisition_id:
                continue
            for line in rec.order_line:
                self.env['s.product.lock'].search([
                    ('purchase_requisition_id', '=', rec.requisition_id.id),
                    ('product_id', '=', line.product_id.id)
                ]).write({'purchase_id': rec.id, 'purchase_line_id': line.id})

    @api.constrains('s_date_approve', 'date_approve', 's_date_order', 'date_order')
    def constrains_date_po(self):
        for rec in self:
            if not rec.date_approve and rec.s_date_approve:
                rec.date_approve = rec.s_date_approve
            if not rec.s_date_approve and rec.date_approve:
                rec.s_date_approve = rec.date_approve
            if not rec.s_date_order and rec.date_order:
                rec.s_date_order = rec.date_order
            if not rec.date_order and rec.s_date_order:
                rec.date_order = rec.s_date_order

    @api.depends('picking_ids.state')
    def _compute_get_purchase_picking(self):
        """
        2. 请购未完成入库单据
        """
        for record in self:
            if record.state != 'purchase':
                record.s_user_purchase_pickings = False
                continue
            order_line = record.order_line.filtered(lambda x: x.product_id.detailed_type == 'product')
            if not order_line:
                record.s_user_purchase_pickings = False
                continue
            product_qty = sum(order_line.mapped('product_qty'))
            qty_received = sum(order_line.mapped('qty_received'))
            if qty_received >= product_qty:
                record.s_user_purchase_pickings = False
            else:
                record.s_user_purchase_pickings = True

    @api.onchange('partner_id')
    def onchange_partner_po(self):
        for record in self:
            if record.partner_id.code in ['99999', '02104']:
                sb_workflow_template_id = self.env['sb.workflow.template'].search([('code', '=', 'GNZF')], limit=1)
                if sb_workflow_template_id:
                    record.sb_workflow_template_id = sb_workflow_template_id.id

    # @api.depends('')
    def _compute_supplement_order(self):
        for rec in self:
            # 是否有未完成收货单
            picking_ids = self.env['stock.picking'].search([('origin', '=', rec.name), ('state', '!=', 'done')])
            # 是否全部收货
            half_received = rec.order_line.filtered(lambda a: a.product_qty > a.qty_received)
            # 已审批&收货单已完成&已收<采购，需要补单
            if rec.sb_workflow_state == 'approve' and not picking_ids and half_received and not rec.subcontracting:
                rec.supplement_order = True
            else:
                rec.supplement_order = False

    def action_supplement_order(self):
        view_id = self.env.ref('sb_workflow_purchase.view_supplement_order_wizard').id
        return {
            'type': 'ir.actions.act_window',
            'name': _('Supplement Order'),
            'view_mode': 'form',
            'res_model': 'supplement.order.wizard',
            'target': 'new',
            'views': [[view_id, 'form']],
        }

    @api.constrains('sb_workflow_state')
    def _check_sb_workflow_state(self):
        for record in self:
            if record.sb_workflow_state == 'approve':
                record.write({'state': 'to confirm'})
            # if record.sb_workflow_state == 'approve' and record.state in ('draft', 'sent', 'to approve', 'to confirm'):
            #     record.button_confirm()
            if record.sb_workflow_state == 'fail_to_approve':
                record.write({'state': 'draft'})

    def action_requisition_view(self):
        self.ensure_one()
        view_id1 = self.env.ref('sb_workflow_purchase.purchase_requisition_view_tree')
        view_id2 = self.env.ref('sb_workflow_purchase.purchase_requisition_view_form')
        return {
            'name': 'Purchase Requisition',
            'type': 'ir.actions.act_window',
            'res_model': 'purchase.requisition',
            'view_mode': 'tree,form',
            'domain': [('id', 'in', self.requisition_ids.ids)],
            'views': [(view_id1.id, 'tree'), (view_id2.id, 'form')],
            'target': 'current',
        }

    # 生成审批流
    def create_workflow(self):
        self.ensure_one()
        if not self.s_date_planned:
            raise UserError(_('Please fill in the expected delivery time!'))
        if self.s_date_planned <= datetime.now().date():
            raise UserError(_('The expected arrival time cannot be less than the current time!'))
        if self.sb_workflow_template_id:
            self.state = 'to approve'
            if len(self.sb_workflow_ids) > 0:
                workflow_sum = len(self.sb_workflow_ids) + 1
            else:
                workflow_sum = 1
            # 创建审批流
            new_wf_id = self.env['sb.workflow'].sudo().create({
                'name': self.name + '- Approval Flow' + '-' + str(workflow_sum),
                'approval_time': fields.Datetime.now(),
                'workflow_type': 'purchase',
                'purchase_order_id': self.id,
            })
            self.sb_workflow_id = new_wf_id.id
            # 根据模板生成审批项
            item_audit_levels = []
            for item in self.sb_workflow_template_id.sb_workflow_template_item_ids:
                new_item = self.env['sb.workflow.item'].sudo().create({
                    'name': item.name,
                    'user_group_ids': item.user_group_ids.ids,
                    'audit_level': item.audit_level,
                    'sb_workflow_id': new_wf_id.id,
                    'state': 'wait_forward'
                })
                # 收集生成的审核项的所有层级
                item_audit_levels.append(new_item.audit_level)
            # 去重排序
            item_audit_levels = list(set(item_audit_levels))
            item_audit_levels.sort()
            # 开放最低层级的神匹配
            for n_item in new_wf_id.sb_workflow_item_ids:
                if n_item.audit_level == item_audit_levels[0]:
                    n_item.state = 'approval'
        else:
            raise ValidationError(_('Please select an approval template.'))

    # 拦截未审批的询价单确认
    def button_confirm(self):
        if not self.sb_workflow_id:
            raise ValidationError(_('The order has not been approved. Please submit for approval first.'))
        if self.sb_workflow_state != 'approve':
            raise ValidationError(_('Order Approval Not Passed！'))
        self.state = 'draft'
        return super(PurchaseOrder, self).button_confirm()

    # 拦截未审批的询价单打印
    def print_quotation(self):
        if not self.sb_workflow_id:
            raise ValidationError(_('The order has not been approved. Please submit for approval first.'))
        if self.sb_workflow_state != 'approve':
            raise ValidationError('Order Approval Not Passed！')
        return super(PurchaseOrder, self).print_quotation()

    # 拦截未审批的询价单发送邮件
    def action_rfq_send(self):
        if not self.sb_workflow_id:
            raise ValidationError(_('The order has not been approved. Please submit for approval first.'))
        if self.sb_workflow_state != 'approve':
            raise ValidationError(_('Order Approval Not Passed！'))
        return super(PurchaseOrder, self).print_quotation()

    # 跳转审批流页面
    def action_workflow(self):
        return {
            'name': 'Approval Flow',
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'form',
            'res_id': self.sb_workflow_id.id,
            # 'views': [(self.env.ref('').id, 'form')],
            'target': 'current',
        }

    # 计算当前单据审批流数
    @api.depends('sb_workflow_ids')
    def _compute_sb_workflow_ids_sum(self):
        for item in self:
            if item.sb_workflow_ids:
                item.sb_workflow_ids_sum = len(item.sb_workflow_ids)
            else:
                item.sb_workflow_ids_sum = 0

    # 跳转当前单据审批流列表
    def action_view_workflow(self):
        return {
            'name': 'Approval Flow',
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'tree',
            # 'res_id': self.sb_workflow_id.id,
            # 'views': [(self.env.ref('').id, 'form')],
            'target': 'current',
            'domain': [('purchase_order_id', '=', self.id)],
        }

    # 采购单恢复草稿需要检查请购单数量是否满足,入不满足,不可恢复草稿状态
    def button_draft(self):
        res = super(PurchaseOrder, self).button_draft()
        if not self.requisition_ids:
            return res
        for line in self.order_line:
            pr = line.pr_middle_ids.pr_line_id
            if sum(pr.mapped('product_qty')) < sum(pr.mapped('qty_received')):
                raise ValidationError(_('请购数量已超出请购数量!'))
            pr.constraint_do_quantity()
            pr.requisition_id.update_state()
        return res

    def button_cancel(self):
        res = super(PurchaseOrder, self).button_cancel()
        # 取消后需要将审批流置空, 并删除未审批的明细
        self.sb_workflow_item_ids.filtered(lambda x: x.state in ['wait_forward', 'approval']).unlink()
        self.sb_workflow_id = False
        if not self.requisition_ids:
            return res
        for line in self.order_line:
            pr = line.pr_middle_ids.pr_line_id
            if sum(pr.mapped('product_qty')) < sum(pr.mapped('qty_received')):
                raise ValidationError(_('请购数量已超出请购数量!'))
            pr.constraint_do_quantity()
            pr.requisition_id.update_state()
        return res

    def write(self, vals):
        if 's_date_planned' in vals:
            s_date_planned = datetime.strptime(vals.get('s_date_planned'), '%Y-%m-%d')
            for rec in self:
                if not rec.s_date_planned:
                    continue
                if rec.state not in ('purchase', 'done'):
                    continue
                if s_date_planned.date() > rec.s_date_planned:
                    raise UserError(_('The estimated arrival time cannot be adjusted later.'))
        return super().write(vals)

    @api.constrains('s_date_planned')
    def constrains_s_date_planned(self):
        for rec in self:
            if rec.s_date_planned:
                rec.order_line.write({'date_planned': rec.s_date_planned})

    def _create_picking(self):
        StockPicking = self.env['stock.picking']
        for order in self.filtered(lambda po: po.state in ('purchase', 'done')):
            if any(product.type in ['product', 'consu'] for product in order.order_line.product_id):
                order = order.with_company(order.company_id)
                # 采购 - 仓库补单
                supplement_id = self.env.context.get('supplement', False)
                if supplement_id:
                    stock_location = self.env['stock.location'].search([('barcode', '=', 'WH-STOCK')])
                    stock_location |= stock_location.mapped('child_internal_location_ids')
                    pickings = order.picking_ids.filtered(
                        lambda x: x.state not in ('done', 'cancel') and x.location_dest_id.id in stock_location.ids)
                else:
                    pickings = order.picking_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
                if not pickings:
                    res = order._prepare_picking()
                    picking = StockPicking.with_user(SUPERUSER_ID).create(res)
                    pickings = picking
                else:
                    picking = pickings[0]
                moves = order.order_line._create_stock_moves(picking)
                moves = moves.filtered(lambda x: x.state not in ('done', 'cancel'))._action_confirm()
                seq = 0
                for move in sorted(moves, key=lambda move: move.date):
                    seq += 5
                    move.sequence = seq
                moves._action_assign()
                # Get following pickings (created by push rules) to confirm them as well.
                forward_pickings = self.env['stock.picking']._get_impacted_pickings(moves)
                (pickings | forward_pickings).action_confirm()
                picking.message_post_with_source(
                    'mail.message_origin_link',
                    render_values={'self': picking, 'origin': order},
                    subtype_xmlid='mail.mt_note',
                )
        return True

    @api.onchange('currency_id')
    @api.constrains('currency_id')
    def onchange_currency_id(self):
        if self.currency_id and self.currency_id.name == 'THB':
            self.order_line._compute_tax_id()
        else:
            for line in self.order_line:
                line.taxes_id = False

    def button_reset_workflow(self):
        self.ensure_one()
        # 查看是否入库,存在入库则不允许重置审批
        # if self.picking_ids.filtered(lambda x: x.state != 'cancel'):
        #     raise UserError(_('存在入库单, 请先取消入库单再操作退回审批!'))
        qty_received = sum(self.order_line.mapped('qty_received'))
        if qty_received > 0:
            raise UserError(_('存在采购入库, 无法执行重置审批'))
        self.state = 'draft'
        self.sb_workflow_id.state = 'return'
        self.sb_workflow_item_ids.filtered(lambda x: x.state in ['wait_forward', 'approval']).update(
            {'state': 'return',
             'approval_time': datetime.now(),
             'approval_instructions': '重走审批',
             'user_id': self.env.user.id})
        self.sb_workflow_id = False


class PurchaseOrderLine(models.Model):
    _inherit = 'purchase.order.line'

    remark = fields.Text(string="Remark")
    do_qty = fields.Float('下单数量(校验使用)', default=0)
    pr_line_id = fields.Many2one('purchase.requisition.line', string='Purchase Requisition Line')
    # 含税单价
    s_price_tax = fields.Float(string='price including tax', compute='_compute_s_price_tax', store=True,
                               digits='Product Price', )
    pr_state = fields.Selection(related='pr_line_id.state')
    pr_middle_ids = fields.One2many('sb.pr.requisition.line.po.line', 'po_line_id', string='PR Line')

    def action_done_requisition(self):
        self.ensure_one()
        # flag = False
        # if not self.env.user.has_group('pcbmanufact.group_stock_cancel_manager'):
        #     flag = True
        #     self.env.user.groups_id.sudo()._apply_group(self.env.ref('pcbmanufact.group_stock_cancel_manager'))
        for m in self.pr_middle_ids:
            m.pr_line_id.write({'state': 'closed'})
            m.pr_line_id.requisition_id.update_state()
        self.s_close = True
        picking_ids = self.order_id.picking_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
        move_ids = picking_ids.move_ids_without_package.filtered(lambda x: x.product_id.id == self.product_id.id)
        for move in move_ids:
            if len(move.picking_id.move_ids_without_package) == 1:
                move.picking_id.sudo().action_cancel()
                continue
            move.unlink()
        # if flag:
        #     self.env.user.groups_id.sudo()._remove_group(self.env.ref('pcbmanufact.group_stock_cancel_manager'))

    # @api.onchange('product_qty', 'product_id')
    @api.constrains('product_qty', 'product_id')
    def onchange_product_qty(self):
        self = self.sudo()
        for record in self:
            if not record.order_id.requisition_ids:
                return
            if record.order_id.state == 'cancel':
                return
            # 把更改掉产品的产品删除掉
            record.pr_middle_ids.filtered(lambda x: x.product_id != record.product_id.product_tmpl_id).unlink()
            middle_ids = record.pr_middle_ids.filtered(lambda x: x.po_state != 'cancel' and
                                                                 x.product_id == record.product_id.product_tmpl_id)
            po_product_qty = record.product_qty
            if not middle_ids:
                # 增加产品, 如果产品存在请购单中, 则给其绑定上请购单行
                pr = record.order_id.requisition_ids.pr_line_ids.filtered(
                    lambda x: x.product_id == record.product_id.product_tmpl_id and
                              x.state in ['draft', 'section_purchasing'])
                if not pr:
                    continue
                for i in pr:
                    product_qty = i.product_qty
                    qty_received = i.qty_received
                    d_qty = product_qty - qty_received
                    if d_qty > po_product_qty:
                        record.write({'pr_middle_ids': [(0, 0, {
                            'pr_line_id': i.id,
                            'do_quantity': po_product_qty,
                        })]})
                    else:
                        record.write({'pr_middle_ids': [(0, 0, {
                            'pr_line_id': i.id,
                            'do_quantity': d_qty,
                        })]})
                        po_product_qty -= d_qty
                if po_product_qty > 0:
                    raise ValidationError(_('订单数量已超额请购数量!'))
                return
            for middle_id in middle_ids:
                pi = middle_id.pr_line_id
                middle_id.write({'do_quantity': po_product_qty})
                if middle_id.pr_product_qty < pi.qty_received:
                    raise ValidationError(_('请购数量已超出请购数量!'))
                pi.constraint_do_quantity()
                pi.requisition_id.update_state()

    @api.depends('price_tax')
    @api.constrains('price_tax')
    def _compute_s_price_tax(self):
        for record in self:
            if record.price_tax > 0:
                record.s_price_tax = round((record.price_tax / record.product_qty) + record.price_unit, 4)
            else:
                record.s_price_tax = record.price_unit

    @api.onchange('product_id')
    @api.constrains('product_id')
    def onchange_product_name(self):
        for record in self:
            if record.product_id and not record.order_id.subcontracting:
                if record.product_id.default_code:
                    record.name = record.product_id.default_code
                elif record.product_id.product_tmpl_id.default_code:
                    record.name = record.product_id.product_tmpl_id.default_code
                else:
                    record.name = record.product_id.name
            if record.order_id.currency_id.name in ['USD', 'CNY']:
                record.taxes_id = False

    def unlink(self):
        """
        控制删除, 需检测请购单, 释放数量
        """
        self.change_line()
        ret = super(PurchaseOrderLine, self).unlink()
        return ret

    def change_line(self):
        for record in self:
            pi = record.pr_middle_ids.pr_line_id
            record.pr_middle_ids.unlink()
            for i in pi:
                i.constraint_do_quantity()
                i.requisition_id.update_state()

    def _prepare_stock_move_vals(self, picking, price_unit, product_uom_qty, product_uom):

        self.ensure_one()
        self._check_orderpoint_picking_type()
        product = self.product_id.with_context(lang=self.order_id.dest_address_id.lang or self.env.user.lang)
        date_planned = self.date_planned or self.order_id.date_planned
        move = {
            # truncate to 2000 to avoid triggering index limit error
            # TODO: remove index in master?
            'name': (self.product_id.display_name or '')[:2000],
            'product_id': self.product_id.id,
            'date': date_planned,
            'date_deadline': date_planned,
            'location_id': self.order_id.partner_id.property_stock_supplier.id,
            'location_dest_id': (self.orderpoint_id and not (self.move_ids | self.move_dest_ids)
                                 and (
                                         picking.location_dest_id.parent_path in self.orderpoint_id.location_id.parent_path))
                                and self.orderpoint_id.location_id.id or self.order_id._get_destination_location(),
            'picking_id': picking.id,
            'partner_id': self.order_id.dest_address_id.id,
            'move_dest_ids': [(4, x) for x in self.move_dest_ids.ids],
            'state': 'draft',
            'purchase_line_id': self.id,
            'company_id': self.order_id.company_id.id,
            'price_unit': price_unit,
            'picking_type_id': self.order_id.picking_type_id.id,
            'group_id': self.order_id.group_id.id,
            'origin': self.order_id.name,
            'description_picking': product.description_pickingin or self.name,
            'propagate_cancel': self.propagate_cancel,
            'warehouse_id': self.order_id.picking_type_id.warehouse_id.id,
            'product_uom_qty': product_uom_qty,
            'product_uom': product_uom.id,
            'product_packaging_id': self.product_packaging_id.id,
            'sequence': self.sequence,
        }

        supplement_id = self.env.context.get('supplement', False)
        if supplement_id:
            supplement_qty = self.env['supplement.order.wizard'].browse(supplement_id).line_ids.filtered(
                lambda a: a.purchase_line_id == self).supplement_qty
            if supplement_qty <= 0:
                return {}
            supplement_qty, _ = self.product_uom._adjust_uom_quantities(supplement_qty, self.product_id.uom_id)
            move.update({
                'product_uom_qty': supplement_qty,
                'quantity': supplement_qty
            })
            # 检查请购单, 恢复请购数量状态
            if self.pr_line_id:
                product_qty = self.product_qty
                qty_received = self.pr_line_id.qty_received
                if qty_received >= product_qty:
                    state = 'purchasing'
                else:
                    state = 'section_purchasing'
                self.pr_line_id.state = state
        return move

    def _prepare_stock_moves(self, picking):
        """ Prepare the stock moves data for one order line. This function returns a list of
        dictionary ready to be used in stock.move's create()
        """
        self.ensure_one()
        res = []
        if self.product_id.type not in ['product', 'consu']:
            return res

        price_unit = self._get_stock_move_price_unit()
        qty = self._get_qty_procurement()

        move_dests = self.move_dest_ids or self.move_ids.move_dest_ids
        move_dests = move_dests.filtered(lambda m: m.state != 'cancel' and not m._is_purchase_return())

        if not move_dests:
            qty_to_attach = 0
            qty_to_push = self.product_qty - qty
        else:
            move_dests_initial_demand = self.product_id.uom_id._compute_quantity(
                sum(move_dests.filtered(
                    lambda m: m.state != 'cancel' and m.location_dest_id.usage != 'supplier').mapped('product_qty')),
                self.product_uom, rounding_method='HALF-UP')
            qty_to_attach = move_dests_initial_demand - qty
            qty_to_push = self.product_qty - move_dests_initial_demand

        if float_compare(qty_to_attach, 0.0, precision_rounding=self.product_uom.rounding) > 0:
            product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(qty_to_attach,
                                                                                   self.product_id.uom_id)
            res.append(self._prepare_stock_move_vals(picking, price_unit, product_uom_qty, product_uom))
        if not float_is_zero(qty_to_push, precision_rounding=self.product_uom.rounding):
            product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(qty_to_push, self.product_id.uom_id)
            extra_move_vals = self._prepare_stock_move_vals(picking, price_unit, product_uom_qty, product_uom)
            if extra_move_vals:
                extra_move_vals['move_dest_ids'] = False  # don't attach
                res.append(extra_move_vals)
        return res

    def write(self, values):
        # 单位仅限管理员更改, 其他人不得更改
        if isinstance(values, list):
            for vals in values:
                self.check_values(vals)
        else:
            self.check_values(values)
        res = super(PurchaseOrderLine, self).write(values)
        return res

    def check_values(self, values):
        if 'product_uom' in values:
            if not self.env.user.has_group('base.group_erp_manager'):
                raise UserError('单位仅限管理员更改, 其他人不得更改')
        if 'product_id' in values:
            if self.product_id.id != values['product_id']:
                new_product = self.env['product.product'].browse(values['product_id'])
                self.order_id.message_post_with_source(
                    'sb_workflow_purchase.track_po_line_product_template',
                    render_values={'line': self, 'new_product': new_product},
                    subtype_xmlid='mail.mt_note',
                )

    def action_add_from_pr(self):
        # 获取请购单下的请购明细
        requisition_ids = self.pr_middle_ids.pr_line_id.requisition_id
        if not requisition_ids:
            return
        pr_ids = requisition_ids.sudo().filtered(lambda x: x.state in ['approved', 'section_purchasing'])
        if not pr_ids:
            return
        pr_line_ids = pr_ids.pr_line_ids.filtered(lambda x: x.state not in ['closed', 'purchasing', 'done'])
        if not pr_line_ids:
            return
        prm = self.env['purchase.requisition.main.wizard'].create({
            'purchase_requisition_ids': pr_ids.ids,
        })
        for pr_line in pr_line_ids:
            if pr_line.product_qty - pr_line.qty_received <= 0:
                continue
            self.env['purchase.requisition.create.wizard'].create({
                'prm_id': prm.id,
                'pr_line_id': pr_line.id,
                'po_id': self.order_id.id,
                'do_quantity': pr_line.product_qty - pr_line.qty_received,
            })
        return {
            'name': _('Add Requisition Line'),
            'type': 'ir.actions.act_window',
            'view_type': 'tree',
            'view_mode': 'tree',
            'res_model': 'purchase.requisition.create.wizard',
            'target': 'new',
            'domain': [('id', 'in', prm.prc_line_ids.ids)],
        }

    @api.constrains('product_qty', 'qty_received')
    def constraint_qty_received(self):
        """验证
        收货数量不得大于采购数量
        """
        for record in self:
            if float_compare(record.qty_received, record.product_qty,
                             precision_rounding=record.product_uom.rounding) == 1:
                raise UserError(_('The received quantity cannot exceed the purchased quantity.'))


class SBPrRequisitionLinePOLine(models.Model):
    _name = 'sb.pr.requisition.line.po.line'
    _description = 'SB PR Requisition Line PO Line'

    po_line_id = fields.Many2one('purchase.order.line', string='PO Line', ondelete='cascade')
    po_state = fields.Selection(related='po_line_id.state', store=True)
    # po_product_qty = fields.Float(related='po_line_id.product_qty', store=True)

    pr_line_id = fields.Many2one('purchase.requisition.line', string='PR Line', ondelete='cascade')
    product_id = fields.Many2one(related='pr_line_id.product_id', store=True)
    pr_product_qty = fields.Float(related='pr_line_id.product_qty', store=True)
    do_quantity = fields.Float('下单数量', default=0)

    @api.constrains('pr_line_id')
    def _constrains_pr_line_id(self):
        for rec in self:
            # 请购单绑定采购单
            rec.pr_line_id.requisition_id.write({'s_purchase_ids': [(4, rec.po_line_id.order_id.id)]})
            # 请购单转采购，补充备料信息
            product_lock_ids = self.env['s.product.lock'].search([
                ('purchase_requisition_line_id', '=', rec.pr_line_id.id),
                ('purchase_id', '=', False)
            ])
            if product_lock_ids:
                product_lock_ids.write({'purchase_id': rec.po_line_id.order_id.id})
